%w{rubygems forwardable oauth hashie httparty json pp}.each{|lib| require lib}

module Twilolia
  include HTTParty
  
  API_VERSION = '1'.freeze
  format :json
  # public method for unauthenticated data
  def self.test
    self.perform_get("/help/test.json")
  end
  
  def self.firehose(options={})
    self.perform_get("/statuses/public_timeline.json")
  end
  
  def self.user(input,options={})
    raise NotFound,"you need id or name to see a user's details" if (input.nil? || input == "") && options[:user_id].nil? && options[:screen_name].nil?
    #
    key= input.is_a?(Integer)? :user_id : :screen_name
    self.perform_get("/users/show.json" , :query => {key => input}.merge(options))
  end
  
  def self.following(id_or_name,options={})
    if id_or_name.nil? || id_or_name == ""
      self.perform_get('/statuses/friends.json',:query => options)
    else
      key =id_or_name.is_a?(Integer)?:user_id : :screen_name
      self.perform_get('/statuses/friends.json',:query => {key => id_or_name}.merge(options))
    end
  end
  
  def self.followers(id_or_name,options={})
    if id_or_name.nil? || id_or_name == ""
      self.perform_get('/statuses/followers.json',:query => options)
    else
      key = id_or_name.is_a?(Integer)? :user_id : :screen_name
      self.perform_get('/statuses/followers.json',:query =>{key => id_or_name}.merge(options))
    end
  end
  
  def self.timeline(input,options={})
    self.perform_get('/statuses/user_timeline.json?'+(input.is_a?(Integer)?'user_id=':'screen_name=')+input,:query => options)
  end
  
  def self.tweet(tweet_id,options={})
    self.perform_get("/statuses/show/#{tweet_id}.json",:query =>  options)
  end
  
  def self.offical_retweets(tweet_id,options={})
    self.perform_get("/statuses/retweets/#{tweet_id}.json",:query => options)
  end
  
  def self.trends(options={})
    self.perform_get("/trends.json",:query => options)
  end
  
  def self.list_timeline(list_owner_screen_name,list_name,options={})
    self.perform_get("/#{list_owner_screen_name}/lists/#{list_name}/statuses.json",:query => options)
  end
  
  #
  def self.api_url
    @api_endpoint ||= 'api.twitter.com'
  end
  
  def self.api_url=(value)
    @api_endpoint =value
  end
  
  # private methods
  private
  
  def self.perform_get(url, options={})
    base_uri self.api_url
    self.make_friendly_type(get(url,options))
  end
  
  def self.make_friendly_type(res)
    raise_errors(res)
    json_data=parse(res)
    # when?
    if json_data && json_data.is_a?(Array) && json_data.first.is_a?(Integer)
      json_data
    else
      mash(json_data)
    end
  end
  
  def self.raise_errors(response)
    case response.code.to_i
      when 400
      data = parse(response)
      raise RateLimitExceeded.new(data), "(#{response.code}): #{response.message} - #{data['error'] if data}"
      when 401
      data = parse(response)
      raise Unauthorized.new(data), "(#{response.code}): #{response.message} - #{data['error'] if data}"
      when 403
      data = parse(response)
      raise General.new(data), "(#{response.code}): #{response.message} - #{data['error'] if data}"
      when 404
      raise NotFound, "(#{response.code}): #{response.message}"
      when 500
      raise InformTwitter, "Twitter had an internal error. Please let them know in the group. (#{response.code}): #{response.message}"
      when 502..503
      raise Unavailable, "(#{response.code}): #{response.message}"
    end
  end
  
  def self.parse(res)
    if res.body.nil? || res.body == ""
      {}
    elsif res.body.is_a?(String)
      {'status' => res.body}
    else
      JSON.parse(res.body)
    end
  end
  
  def self.mash(json_data)
    if json_data.is_a?(Array)
      json_data.map{|item| make_mash_with_consistent_hash(item)}
    elsif json_data.is_a?(Hash)
      make_mash_with_consistent_hash(json_data)
    else
      data
    end
  end
  
  def self.make_mash_with_consistent_hash(data)
    m=Hashie::Mash.new(data)
    def m.hash
      inspect.hash
    end
    return m
  end
  
  class TwitterError < StandardError
    attr_reader :data
    def initialize(date)
      @data=data
      super
    end
  end
  
  class RateLimitExceeded < TwitterError;end
  
  class Unauthorized < TwitterError;end
  
  class General < TwitterError;end
  
  class Unavailable < StandardError;end
  
  class InformTwitter < StandardError;end
  
  class NotFound < StandardError;end
end

directory = File.expand_path(File.dirname(__FILE__))

%w{httpauth base oauth request}.each{|filename| require File.join(directory,"twilolia",filename)}